using System;
using System.Collections.Generic;
using System.Text;
using ACS.Compiler.Tables;
using ACS.Compiler.Tables.Modes;
using ACS.Compiler.Util;
using ACS.Compiler.Synthes;
using ACS.Compiler.Tree.Interfaces;

namespace ACS.Compiler.Tree.Impl
{
    public class Call : Statement, IExpression
    {
        private string _name;
        private string _namespace;
        private List<IExpression> _parameters = new List<IExpression>();
        private Procedure _procedure;
        private Mode _mode;

        public override void MatchCallParameter(CallParameter call)
        {
            _parameters.Add(call.Parameter);
        }

        public override void MatchIdent(Ident ident)
        {
            _name = ident.ToString();
        }

        public override void MatchSelect(Select select)
        {
            if (select.Left != null && select.Right != null)
            {
                if (select.Left.Tag == TreeTags.Ident &&
                    select.Right.Tag == TreeTags.Ident)
                {
                    _name = select.Right.ToString();
                    _namespace = select.Left.ToString();
                    return;
                }
            }
            Logger.Instance.TraceError("Wrong proc name (more then one namespace possibly...)");
        }

        public string Name
        {
            get { return _name; }
        }

        public string Namespace
        {
            get { return _namespace; }
        }

        public List<IExpression> Parameters
        {
            get { return _parameters; }
        }

        public override Statements StatementType
        {
            get { return Statements.Call; }
        }

        public override void Resolve(string ns, DataTable localData)
        {
            ModeResolveExpression(ns, localData, null, true);
        }

        public override void Format()
        {
            Formatter.Current.Format(ToDeclareString() +";");
        }

        public Expression.Expressions ExpressionType
        {
            get
            {
                return Expression.Expressions.Call;
            }
        }

        public Mode Mode
        {
            get
            {
                return _mode;
            }
        }
        public Mode ModeResolveExpression(string ns, DataTable localData, Mode parentMode)
        {
            return ModeResolveExpression(ns, localData, null, false);
        }

        private string ParamsToString()
        {
            StringBuilder par = new StringBuilder();
            bool first = true;
            foreach (IExpression ex in _parameters)
            {
                if (!first)
                {
                    par.Append(",");
                }
                first = false;
                par.Append(ex.ToDeclareString());
            }
            return par.ToString();
        }

        public string ToDeclareString()
        {
            if (_procedure.Namespace == Formatter.Current.Namespace || _procedure.Standard)
            {
                return _procedure.Name + "(" + ParamsToString() + ")";
            }
            else
            {
                return String.Format("{0}.Instance.{1}({2})", _procedure.Namespace, _procedure.Name, ParamsToString());
            }
        }


        public Mode ModeResolveExpression(string ns, DataTable localData, Mode parentMode, bool mayBeNull)
        {
            _procedure = ProcTable.Instance.FindProc(_namespace??ns, _name);
            if (_procedure == null)
            {
                Logger.Instance.TraceError("Proc not found", this);
            }
            if (Parameters.Count != _procedure.Parameters.Count)
            {
                Logger.Instance.TraceError("Wrong parameters number.", this);
            }
            for(int i =0; i< Parameters.Count; i++)
            {
                Mode formalMode = _procedure.Parameters[i].Mode;
                IExpression actualParameter = Parameters[i];
                Mode actualMode = actualParameter.ModeResolveExpression(ns, localData, null);
                if (!ModesTable.Castable(actualMode, formalMode))
                {
                    Logger.Instance.TraceModesNotEquals(formalMode, actualMode, this);
                }
            }
            _mode = _procedure.RetMode;
            if (_mode == null && !mayBeNull)
            {
                Logger.Instance.TraceError("Proc returns null", this);
            }
            return _mode;
        }
    }
}
